package com.pospi.pai.yunpos.util;

import android.content.Context;
import android.database.DataSetObserver;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListAdapter;
import android.widget.Scroller;

import java.util.LinkedList;
import java.util.Queue;

/**
 * Created by acer on 2018/8/14.
 */

public class HorizontalListView extends AdapterView<ListAdapter> {


    protected ListAdapter mAdapter;

    /**
     * 左侧view的序号
     */

    private int mLeftViewIndex = -1;

    /**
     * 右侧view的序号
     */

    private int mRightViewIndex = 0;

    /**
     * 当前显示的序号
     */

    protected int mCurrentX;

    /**
     * 下一个显示的序号
     */

    protected int mNextX;

    private int mMaxX = Integer.MAX_VALUE;

    private int mDisplayOffset = 0;

    protected Scroller mScroller;

    private GestureDetector mGesture;

    private Queue<View> mRemovedViewQueue = new LinkedList<View>();

    private OnItemSelectedListener mOnItemSelected;

    private OnItemClickListener mOnItemClicked;

    private OnItemLongClickListener mOnItemLongClicked;

    private OnScrollLeftListener mOnScrollLeftListener;

    /**
     * 数据是否改变
     */

    private boolean mDataChanged = false;


    public HorizontalListView(Context context) {

        this(context, null);

    }


    public HorizontalListView(Context context, AttributeSet attrs) {

        super(context, attrs);

        initView();

    }


    /**
     * 初始化视图
     */

    private synchronized void initView() {

        mLeftViewIndex = -1;

        mRightViewIndex = 0;

        mDisplayOffset = 0;

        mCurrentX = 0;

        mNextX = 0;

        mScroller = new Scroller(getContext());

        mGesture = new GestureDetector(getContext(), mOnGesture);

    }


    @Override

    public void setOnItemSelectedListener(AdapterView.OnItemSelectedListener listener) {

        mOnItemSelected = listener;

    }


    @Override

    public void setOnItemClickListener(AdapterView.OnItemClickListener listener) {

        mOnItemClicked = listener;

    }


    @Override

    public void setOnItemLongClickListener(AdapterView.OnItemLongClickListener listener) {

        mOnItemLongClicked = listener;

    }


    /**
     * 设置滑动到最左边监听器
     *
     * @param listener
     */

    public void setOnScrollLeftListener(OnScrollLeftListener listener) {

        mOnScrollLeftListener = listener;

    }


    private DataSetObserver mDataObserver = new DataSetObserver() {

        @Override

        public void onChanged() {

            synchronized (HorizontalListView.this) {

                mDataChanged = true;

            }

            invalidate();

            requestLayout();

        }


        @Override

        public void onInvalidated() {

            reset();

            invalidate();

            requestLayout();

        }

    };


    @Override

    public ListAdapter getAdapter() {

        return mAdapter;

    }


    @Override

    public View getSelectedView() {

        // TODO: implement

        return null;

    }


    @Override

    public void setAdapter(ListAdapter adapter) {

        if (mAdapter != null) {

            mAdapter.unregisterDataSetObserver(mDataObserver);

        }

        mAdapter = adapter;

        mAdapter.registerDataSetObserver(mDataObserver);

        reset();

    }


    private synchronized void reset() {

        initView();

        removeAllViewsInLayout();

        requestLayout();

    }


    @Override

    public void setSelection(int position) {

        // TODO: implement

    }


    private void addAndMeasureChild(final View child, int viewPos) {

        LayoutParams params = child.getLayoutParams();

        if (params == null) {

            params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);

        }


        addViewInLayout(child, viewPos, params, true);

        child.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST));

    }


    public interface OnScrollLeftListener {

        /**
         * 滑动到了最左边
         */

        void onScrollLeft();

        /**
         * 滑出了最左边
         */

        void onScrollOutLeft();

    }


    @Override

    protected synchronized void onLayout(boolean changed, int left, int top, int right, int bottom) {

        super.onLayout(changed, left, top, right, bottom);

        // 滑动到最左边回调方法

        if (mOnScrollLeftListener != null) {

            if (mScroller.getCurrX() < 30) {

                mOnScrollLeftListener.onScrollLeft();

            } else {

                mOnScrollLeftListener.onScrollOutLeft();

            }

        }


        if (mAdapter == null) {

            return;

        }


        if (mDataChanged) {

            int oldCurrentX = mCurrentX;

            initView();

            removeAllViewsInLayout();

            mNextX = oldCurrentX;

            mDataChanged = false;

        }


        if (mScroller.computeScrollOffset()) {

            int scrollx = mScroller.getCurrX();

            mNextX = scrollx;

        }


        if (mNextX <= 0) {

            mNextX = 0;

            mScroller.forceFinished(true);

        }

        if (mNextX >= mMaxX) {

            mNextX = mMaxX;

            mScroller.forceFinished(true);

        }


        int dx = mCurrentX - mNextX;


        removeNonVisibleItems(dx);

        fillList(dx);

        positionItems(dx);


        mCurrentX = mNextX;


        if (!mScroller.isFinished()) {

            post(new Runnable() {

                @Override

                public void run() {

                    requestLayout();

                }

            });


        }

    }


    private void fillList(final int dx) {

        int edge = 0;

        View child = getChildAt(getChildCount() - 1);

        if (child != null) {

            edge = child.getRight();

        }

        fillListRight(edge, dx);


        edge = 0;

        child = getChildAt(0);

        if (child != null) {

            edge = child.getLeft();

        }

        fillListLeft(edge, dx);


    }


    private void fillListRight(int rightEdge, final int dx) {

        while (rightEdge + dx < getWidth() && mRightViewIndex < mAdapter.getCount()) {

            View child = mAdapter.getView(mRightViewIndex, mRemovedViewQueue.poll(), this);

            addAndMeasureChild(child, -1);

            rightEdge += child.getMeasuredWidth();


            if (mRightViewIndex == mAdapter.getCount() - 1) {

                mMaxX = mCurrentX + rightEdge - getWidth();

            }


            if (mMaxX < 0) {

                mMaxX = 0;

            }

            mRightViewIndex++;

        }


    }


    private void fillListLeft(int leftEdge, final int dx) {

        while (leftEdge + dx > 0 && mLeftViewIndex >= 0) {

            View child = mAdapter.getView(mLeftViewIndex, mRemovedViewQueue.poll(), this);

            addAndMeasureChild(child, 0);

            leftEdge -= child.getMeasuredWidth();

            mLeftViewIndex--;

            mDisplayOffset -= child.getMeasuredWidth();

        }

    }


    private void removeNonVisibleItems(final int dx) {

        View child = getChildAt(0);

        while (child != null && child.getRight() + dx <= 0) {

            mDisplayOffset += child.getMeasuredWidth();

            mRemovedViewQueue.offer(child);

            removeViewInLayout(child);

            mLeftViewIndex++;

            child = getChildAt(0);


        }


        child = getChildAt(getChildCount() - 1);

        while (child != null && child.getLeft() + dx >= getWidth()) {

            mRemovedViewQueue.offer(child);

            removeViewInLayout(child);

            mRightViewIndex--;

            child = getChildAt(getChildCount() - 1);

        }

    }


    private void positionItems(final int dx) {

        if (getChildCount() > 0) {

            mDisplayOffset += dx;

            int left = mDisplayOffset;

            for (int i = 0; i < getChildCount(); i++) {

                View child = getChildAt(i);

                int childWidth = child.getMeasuredWidth();

                child.layout(left, 0, left + childWidth, child.getMeasuredHeight());

                left += childWidth + child.getPaddingRight();

            }

        }

    }


    public synchronized void scrollTo(int x) {

        mScroller.startScroll(mNextX, 0, x - mNextX, 0);

        requestLayout();

    }


    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {

        getParent().requestDisallowInterceptTouchEvent(true);

        boolean handled = super.dispatchTouchEvent(ev);

        handled |= mGesture.onTouchEvent(ev);

        return handled;

    }


    protected boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {

        synchronized (HorizontalListView.this) {

            mScroller.fling(mNextX, 0, (int) -velocityX, 0, 0, mMaxX, 0, 0);

        }

        requestLayout();

        return true;

    }


    protected boolean onDown(MotionEvent e) {

        mScroller.forceFinished(true);

        return true;

    }


    private GestureDetector.SimpleOnGestureListener mOnGesture = new GestureDetector.SimpleOnGestureListener() {

        @Override

        public boolean onDown(MotionEvent e) {

            return HorizontalListView.this.onDown(e);

        }


        @Override

        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {

            return HorizontalListView.this.onFling(e1, e2, velocityX, velocityY);

        }


        @Override

        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {

            synchronized (HorizontalListView.this) {

                mNextX += (int) distanceX;

            }

            requestLayout();


            return true;

        }


        @Override

        public boolean onSingleTapConfirmed(MotionEvent e) {

            for (int i = 0; i < getChildCount(); i++) {

                View child = getChildAt(i);

                if (isEventWithinView(e, child)) {

                    if (mOnItemClicked != null) {

                        mOnItemClicked.onItemClick(HorizontalListView.this, child, mLeftViewIndex + 1 + i,

                                mAdapter.getItemId(mLeftViewIndex + 1 + i));

                    }

                    if (mOnItemSelected != null) {

                        mOnItemSelected.onItemSelected(HorizontalListView.this, child, mLeftViewIndex + 1

                                + i, mAdapter.getItemId(mLeftViewIndex + 1 + i));

                    }

                    break;

                }


            }

            return true;

        }


        @Override

        public void onLongPress(MotionEvent e) {

            int childCount = getChildCount();

            for (int i = 0; i < childCount; i++) {

                View child = getChildAt(i);

                if (isEventWithinView(e, child)) {

                    if (mOnItemLongClicked != null) {

                        mOnItemLongClicked.onItemLongClick(HorizontalListView.this, child, mLeftViewIndex

                                + 1 + i, mAdapter.getItemId(mLeftViewIndex + 1 + i));

                    }

                    break;

                }


            }

        }


        /**

         * 判断点击落点是否在指定的View内部

         * @param e

         * @param child

         * @return

         */

        private boolean isEventWithinView(MotionEvent e, View child) {

            Rect viewRect = new Rect();

            int[] childPosition = new int[2];

            child.getLocationOnScreen(childPosition);

            int left = childPosition[0];

            int right = left + child.getWidth();

            int top = childPosition[1];

            int bottom = top + child.getHeight();

            viewRect.set(left, top, right, bottom);

            return viewRect.contains((int) e.getRawX(), (int) e.getRawY());

        }

    };

}